#include <vector>
#include "cartographer/common/configuration_file_resolver.h"
#include "cartographer/common/port.h"
#include "cartographer/mapping/map_builder_interface.h"
#include "cartographer/rcm/dynamic/options.h"
#include "cartographer/rcm/imu/options.h"
#include "cartographer_ros/node_options.h"
#include "glog/logging.h"

namespace cartographer_ros
{
    NodeOptions CreateNodeOptions(::cartographer::common::LuaParameterDictionary *const lua_parameter_dictionary)
    {
        NodeOptions options;
        options.map_builder_options =
            ::cartographer::mapping::CreateMapBuilderOptions(lua_parameter_dictionary->GetDictionary("map_builder").get());
        options.map_frame = lua_parameter_dictionary->GetString("map_frame");
        options.lookup_transform_timeout_sec = lua_parameter_dictionary->GetDouble("lookup_transform_timeout_sec");
        options.submap_publish_period_sec = lua_parameter_dictionary->GetDouble("submap_publish_period_sec");
        options.pose_publish_period_sec = lua_parameter_dictionary->GetDouble("pose_publish_period_sec");
        options.trajectory_publish_period_sec = lua_parameter_dictionary->GetDouble("trajectory_publish_period_sec");
        if (lua_parameter_dictionary->HasKey("publish_to_tf"))
        {
            options.publish_to_tf = lua_parameter_dictionary->GetBool("publish_to_tf");
        }
        if (lua_parameter_dictionary->HasKey("publish_tracked_pose"))
        {
            options.publish_tracked_pose = lua_parameter_dictionary->GetBool("publish_tracked_pose");
        }
        if (lua_parameter_dictionary->HasKey("use_pose_extrapolator"))
        {
            options.use_pose_extrapolator = lua_parameter_dictionary->GetBool("use_pose_extrapolator");
        }
        return options;
    }

    std::tuple<NodeOptions, TrajectoryOptions> LoadOptions(const std::string &configuration_directory,
                                                           const std::string &configuration_basename)
    {
        auto file_resolver =
            absl::make_unique<cartographer::common::ConfigurationFileResolver>(std::vector<std::string>{configuration_directory});
        const std::string code = file_resolver->GetFileContentOrDie(configuration_basename);
        cartographer::common::LuaParameterDictionary lua_parameter_dictionary(code, std::move(file_resolver));

        auto &imuopt = cartographer::ImuOptions::GetInstance();
        imuopt.noise_acc = lua_parameter_dictionary.GetDouble("imu_noise_acc");
        imuopt.noise_gyro = lua_parameter_dictionary.GetDouble("imu_noise_gyro");
        imuopt.use_imu_optimize = lua_parameter_dictionary.GetBool("use_imu_optimize");
        imuopt.use_imu_integrate = lua_parameter_dictionary.GetBool("use_imu_integrate");
        imuopt.imu_opt_use_identity_information = lua_parameter_dictionary.GetBool("imu_opt_use_identity_information");
        imuopt.walk_acc = lua_parameter_dictionary.GetDouble("imu_walk_acc");
        imuopt.optimizer_win_size = lua_parameter_dictionary.GetInt("optimizer_win_size");
        imuopt.walk_gyro = lua_parameter_dictionary.GetDouble("imu_walk_gyro");
        imuopt.frequency = lua_parameter_dictionary.GetDouble("imu_frequency");

        auto &dynopt = cartographer::DynamicOptions::GetInstance();
        dynopt.cacheSize = lua_parameter_dictionary.GetInt("cacheSize");
        dynopt.motionSize = lua_parameter_dictionary.GetInt("motionSize");
        dynopt.dynScaleIterTimes = lua_parameter_dictionary.GetInt("dynScaleIterTimes");
        dynopt.dynValidMotionTimes = lua_parameter_dictionary.GetInt("dynValidMotionTimes");

        dynopt.dynEnable = lua_parameter_dictionary.GetBool("dynEnable");
        dynopt.dynNearNum = lua_parameter_dictionary.GetInt("dynNearNum");
        dynopt.hitRatio = lua_parameter_dictionary.GetDouble("hitRatio");
        dynopt.dynNearRadius = lua_parameter_dictionary.GetDouble("dynNearRadius");
        dynopt.subpixelScale = lua_parameter_dictionary.GetDouble("subpixelScale");
        dynopt.dynScaleStride = lua_parameter_dictionary.GetDouble("dynScaleStride");
        dynopt.dynDetectDistMax = lua_parameter_dictionary.GetDouble("dynDetectDistMax");

        auto &imufilteropt = cartographer::ImuOptionsFilter::GetInstance();
        imufilteropt.filterGyro = lua_parameter_dictionary.GetBool("imufilter_filterGyro");
        imufilteropt.intervalMax = lua_parameter_dictionary.GetDouble("imufilter_intervalMax");
        imufilteropt.threshAccHorizon = lua_parameter_dictionary.GetDouble("imufilter_threshAccHorizon");
        imufilteropt.threshGyroHorizon = lua_parameter_dictionary.GetDouble("imufilter_threshGyroHorizon");
        imufilteropt.threshVerticalRatio = lua_parameter_dictionary.GetDouble("imufilter_threshVerticalRatio");

        auto &cloopt = cartographer::ClosureCheckOption::GetInstance();
        cloopt.closure_check_active = lua_parameter_dictionary.GetBool("closure_check_active");
        cloopt.closure_check_socre_min = lua_parameter_dictionary.GetInt("closure_check_socre_min");
        cloopt.closure_check_histogram_min_num = lua_parameter_dictionary.GetInt("closure_check_histogram_min_num");
        cloopt.closure_check_uncontinuous_min_num = lua_parameter_dictionary.GetInt("closure_check_uncontinuous_min_num");
        cloopt.closure_check_histogram_resolution = lua_parameter_dictionary.GetDouble("closure_check_histogram_resolution");

        return std::make_tuple(CreateNodeOptions(&lua_parameter_dictionary), CreateTrajectoryOptions(&lua_parameter_dictionary));
    }

} // namespace cartographer_ros
